home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 7
/
Apprentice-Release7.iso
/
Environments
/
PowerLisp 2.01
/
Supplemental Documentation
/
Documentation
/
Chapter 09. Declarations
< prev
next >
Wrap
Text File
|
1995-03-27
|
51KB
|
1,152 lines
Common Lisp the Language, 2nd Edition
-------------------------------------------------------------------------------
9. Declarations
Declarations allow you to specify extra information about your program to the
Lisp system. With one exception, declarations are completely optional and
correct declarations do not affect the meaning of a correct program. The
exception is that special declarations do affect the interpretation of variable
bindings and references and so must be specified where appropriate. All other
declarations are of an advisory nature, and may be used by the Lisp system to
aid the programmer by performing extra error checking or producing more
efficient compiled code. Declarations are also a good way to add documentation
to a program.
Note that it is considered an error for a program to violate a declaration
(such as a type declaration), but an implementation is not required to detect
such errors (though such detection, where feasible, is to be encouraged).
-------------------------------------------------------------------------------
* Declaration Syntax
* Declaration Specifiers
* Type Declaration for Forms
-------------------------------------------------------------------------------
9.1. Declaration Syntax
The declare construct is used for embedding declarations within executable
code. Global declarations and declarations that are computed by a program are
established by the proclaim construct.
[change_begin]
X3J13 voted in June 1989 (PROCLAIM-ETC-IN-COMPILE-FILE) to introduce the new
macro declaim, which is guaranteed to be recognized appropriately by the
compiler and is often more convenient than proclaim for establishing global
declarations.
[change_end]
[Special Form]
declare {decl-spec}*
A declare form is known as a declaration. Declarations may occur only at the
beginning of the bodies of certain special forms; that is, a declaration may
occur only as a statement of such a special form, and all statements preceding
it (if any) must also be declare forms (or possibly documentation strings, in
some cases). Declarations may occur in lambda-expressions and in the forms
listed here.
define-setf-method labels
defmacro let
defsetf let*
deftype locally
defun macrolet
do multiple-value-bind
do* prog
do-all-symbols prog*
do-external-symbols with-input-from-string
do-symbols with-open-file
dolist with-open-stream
dotimes with-output-to-string
flet
[change_begin]
Notice of correction. In the first edition, the above list failed to mention
the forms define-setf-method, with-input-from-string, with-open-file,
with-open-stream, and with-output-to-string, even though their individual
descriptions in the first edition specified that declarations may appear in
those forms.
[change_end]
X3J13 voted in June 1989 (CONDITION-RESTARTS) to add with-condition-restarts
and also (DATA-IO) to add print-unreadable-object and
with-standard-io-syntax. The X3J13 vote left it unclear whether these macros
permit declarations to appear at the heads of their bodies. I believe that was
the intent, but this is only my interpretation.
[change_begin]
X3J13 voted in June 1988 (CLOS) to adopt the Common Lisp Object System, which
includes the following additional forms in which declarations may occur:
defgeneric generic-function
define-method-combination generic-labels
defmethod with-added-methods
generic-flet
Furthermore X3J13 voted in January 1989 (SYMBOL-MACROLET-DECLARE) to allow
declarations to occur before the bodies of these forms:
symbol-macrolet with-slots
with-accessors
There are certain aspects peculiar to symbol-macrolet (and therefore also to
with-accessors and with-slots, which expand into uses of symbol-macrolet). An
error is signaled if a name defined by symbol-macrolet is declared special, and
a type declaration of a name defined by symbol-macrolet is equivalent in effect
to wrapping a the form mentioning that type around the expansion of the defined
symbol.
[change_end]
It is an error to attempt to evaluate a declaration. Those special forms that
permit declarations to appear perform explicit checks for their presence.
-------------------------------------------------------------------------------
Compatibility note: In MacLisp, declare is a special form that does nothing but
return the symbol declare as its result. The MacLisp interpreter knows nothing
about declarations but just blindly evaluates them, effectively ignoring them.
The MacLisp compiler recognizes declarations but processes them simply by
evaluating the subforms of the declaration in the compilation context. In
Common Lisp it is important that both the interpreter and compiler recognize
declarations (especially special declarations) and treat them consistently, and
so the rules about the structure and use of declarations have been made
considerably more stringent. The odd tricks played in MacLisp by writing
arbitrary forms to be evaluated within a declare form are better done in both
MacLisp and Common Lisp by using eval-when.
-------------------------------------------------------------------------------
It is permissible for a macro call to expand into a declaration and be
recognized as such, provided that the macro call appears where a declaration
may legitimately appear. (However, a macro call may not appear in place of a
decl-spec.)
[change_begin]
X3J13 voted in March 1988 (DECLARE-MACROS) to eliminate the recognition of a
declaration resulting from the expansion of a macro call. This feature proved
to be seldom used and awkward to implement in interpreters, compilers, and
other code-analyzing programs.
Under this change, a declaration is recognized only as such if it appears
explicitly, as a list whose car is the symbol declare, in the body of a
relevant special form. (Note, however, that it is still possible for a macro to
expand into a call to the proclaim function.)
[change_end]
Each decl-spec is a list whose car is a symbol specifying the kind of
declaration to be made. Declarations may be divided into two classes: those
that concern the bindings of variables, and those that do not. (The special
declaration is the sole exception: it effectively falls into both classes, as
explained below.) Those that concern variable bindings apply only to the
bindings made by the form at the head of whose body they appear. For example,
in
(defun foo (x)
(declare (type float x)) ...
(let ((x 'a)) ...)
...)
the type declaration applies only to the outer binding of x, and not to the
binding made in the let.
-------------------------------------------------------------------------------
Compatibility note: This represents a difference from MacLisp, in which type
declarations are pervasive.
-------------------------------------------------------------------------------
Declarations that do not concern themselves with variable bindings are
pervasive, affecting all code in the body of the special form. As an example of
a pervasive declaration,
(defun foo (x y) (declare (notinline floor)) ...)
advises that everywhere within the body of foo the function floor should not be
open-coded but called as an out-of-line subroutine.
Some special forms contain pieces of code that, properly speaking, are not part
of the body of the special form. Examples of this are initialization forms that
provide values for bound variables, and the result forms of iteration
constructs. In all cases such additional code is within the scope of any
pervasive declarations appearing before the body of the special form.
Non-pervasive declarations have no effect on such code, except (of course) in
those situations where the code is defined to be within the scope of the
variables affected by such non-pervasive declarations. For example:
(defun few (x &optional (y *print-circle*))
(declare (special *print-circle*))
...)
The reference to *print-circle* in the first line of this example is special
because of the declaration in the second line.
(defun nonsense (k x z)
(foo z x) ;First call to foo
(let ((j (foo k x)) ;Second call to foo
(x (* k k)))
(declare (inline foo) (special x z))
(foo x j z))) ;Third call to foo
In this rather nonsensical example, the inline declaration applies to the
second and third calls to foo, but not to the first one. The special
declaration of x causes the let form to make a special binding for x and causes
the reference to x in the body of the let to be a special reference. The
reference to x in the second call to foo is also a special reference. The
reference to x in the first call to foo is a local reference, not a special
one. The special declaration of z causes the reference to z in the call to foo
to be a special reference; it will not refer to the parameter to nonsense named
z, because that parameter binding has not been declared to be special. (The
special declaration of z does not appear in the body of the defun, but in an
inner construct, and therefore does not affect the binding of the parameter.)
[change_begin]
X3J13 voted in January 1989 (DECLARATION-SCOPE) to replace the rules
concerning the scope of declarations occurring at the head of a special form or
lambda-expression:
* The scope of a declaration always includes the body forms, as well as any
``stepper'' or ``result'' forms (which are logically part of the body), of
the special form or lambda-expression.
* If the declaration applies to a name binding, then the scope of the
declaration also includes the scope of the name binding.
Note that the distinction between pervasive and non-pervasive declarations is
eliminated. An important change from the first edition is that
``initialization'' forms are specifically not included as part of the body
under the first rule; on the other hand, in many cases initialization forms may
fall within the scope of certain declarations under the second rule.
X3J13 also voted in January 1989 (DECLARE-TYPE-FREE) to change the
interpretation of type declarations (see section 9.2).
These changes affect the interpretation of some of the examples from the first
edition.
(defun foo (x)
(declare (type float x)) ...
(let ((x 'a)) ...)
...)
Under the interpretation approved by X3J13, the type declaration applies to
both bindings of x. More accurately, the type declaration is considered to
apply to variable references rather than bindings, and the type declaration
refers to every reference in the body of foo to a variable named x, no matter
to what binding it may refer.
(defun foo (x y) (declare (notinline floor)) ...)
This example of the use of notinline stands unchanged, but the following slight
extension of it would change:
(defun foo (x &optional (y (floor x)))
(declare (notinline floor)) ...)
Under first edition rules, the notinline declaration would be considered to
apply to the call to floor in the initialization form for y. Under the
interpretation approved by X3J13, the notinline would not apply to that
particular call to floor. Instead the user must write something like
(defun foo (x &optional (y (locally (declare (notinline floor))
(floor x))))
(declare (notinline floor)) ...)
or perhaps
(locally (declare (notinline floor))
(defun foo (x &optional (y (floor x))) ...))
Similarly, the special declaration in
(defun few (x &optional (y *print-circle*))
(declare (special *print-circle*))
...)
is not considered to apply to the reference in the initialization form for y in
few. As for the nonsense example,
(defun nonsense (k x z)
(foo z x) ;First call to foo
(let ((j (foo k x)) ;Second call to foo
(x (* k k)))
(declare (inline foo) (special x z))
(foo x j z))) ;Third call to foo
under the interpretation approved by X3J13, the inline declaration is no longer
considered to apply to the second call to foo, because it is in an
initialization form, which is no longer considered in the scope of the
declaration. Similarly, the reference to x in that second call to foo is no
longer taken to be a special reference, but a local reference to the second
parameter of nonsense.
[change_end]
[old_change_begin]
[Macro]
locally {declaration}* {form}*
This macro may be used to make local pervasive declarations where desired. It
does not bind any variables and therefore cannot be used meaningfully for
declarations of variable bindings. (Note that the special declaration may be
used with locally to pervasively affect references to, rather than bindings of,
variables.) For example:
(locally (declare (inline floor) (notinline car cdr))
(declare (optimize space))
(floor (car x) (cdr y)))
[old_change_end]
[change_begin]
X3J13 voted in January 1989 (RETURN-VALUES-UNSPECIFIED) to specify that
locally executes the forms as an implicit progn and returns the value(s) of the
last form.
X3J13 voted in March 1989 (LOCALLY-TOP-LEVEL) to make locally be a special
form rather than a macro. It still has the same syntax.
[Special Form]
locally {declaration}* {form}*
This change was made to accommodate the new compilation model for top-level
forms in a file (see section 25.1). When a locally form appears at top level,
the forms in its body are processed as top-level forms. This means that one
may, for example, meaningfully use locally to wrap declarations around a defun
or defmacro form:
(locally
(declare (optimize (safety 3) (space 3) (debug 3) (speed 1)))
(defun foo (x &optional (y (abs x)) (z (sqrt y)))
(bar x y z)))
Without assurance that this works one must write something cumbersome such as
(defun foo (x &optional (y (locally
(declare (optimize (safety 3)
(space 3)
(debug 3)
(speed 1)))
(abs x)))
(z (locally
(declare (optimize (safety 3)
(space 3)
(debug 3)
(speed 1)))
(sqrt y))))
(locally
(declare (optimize (safety 3) (space 3) (debug 3) (speed 1)))
(bar x y z)))
[change_end]
[Function]
proclaim decl-spec
The function proclaim takes a decl-spec as its argument and puts it into effect
globally. (Such a global declaration is called a proclamation.) Because
proclaim is a function, its argument is always evaluated. This allows a program
to compute a declaration and then put it into effect by calling proclaim.
Any variable names mentioned are assumed to refer to the dynamic values of the
variable. For example, the proclamation
(proclaim '(type float tolerance))
once executed, specifies that the dynamic value of tolerance should always be a
floating-point number. Similarly, any function-names mentioned are assumed to
refer to the global function definition.
A proclamation constitutes a universal declaration, always in force unless
locally shadowed. For example,
(proclaim '(inline floor))
advises that floor should normally be open-coded in-line by the compiler (but
in the situation
(defun foo (x y) (declare (notinline floor)) ...)
it will be compiled out-of-line anyway in the body of foo, because of the
shadowing local declaration to that effect).
[change_begin]
X3J13 voted in January 1989 (SPECIAL-TYPE-SHADOWING) to clarify that such
shadowing does not occur in the case of type declarations. If there is a local
type declaration for a special variable and there is also a global proclamation
for that same variable, then the value of the variable within the scope of the
local declaration must be a member of the intersection of the two declared
types. This is consistent with the treatment of nested local type declarations
on which X3J13 also voted in January 1989 (DECLARE-TYPE-FREE) .
[change_end]
As a special case (so to speak), proclaim treats a special decl-spec as
applying to all bindings as well as to all references of the mentioned
variables.
[change_begin]
Notice of correction. In the first edition, this sentence referred to a
``special declaration-form.'' That was incorrect; proclaim accepts only a
decl-spec, not a declaration-form.
[change_end]
For example, after
(proclaim '(special x))
in a function definition such as
(defun example (x) ...)
the parameter x will be bound as a special (dynamic) variable rather than as a
lexical (static) variable. This facility should be used with caution. The usual
way to define a globally special variable is with defvar or defparameter.
[change_begin]
X3J13 voted in June 1989 (PROCLAIM-ETC-IN-COMPILE-FILE) to clarify that the
compiler is not required to treat calls to proclaim any differently from the
way it treats any other function call. If a top-level call to proclaim is to
take effect at compile time, it should be surrounded by an appropriate
eval-when form. Better yet, the new macro declaim may be used instead.
[Macro]
declaim {decl-spec}*
This macro is syntactically like declare and semantically like proclaim. It is
an executable form and may be used anywhere proclaim may be called. However,
each decl-spec is not evaluated.
If a call to this macro appears at top level in a file being processed by the
file compiler, the proclamations are also made at compile time. As with other
defining macros, it is unspecified whether or not the compile-time side effects
of a declaim persist after the file has been compiled (see section 25.1).
[change_end]
-------------------------------------------------------------------------------
9.2. Declaration Specifiers
Here is a list of valid declaration specifiers for use in declare. A construct
is said to be ``affected'' by a declaration if it occurs within the scope of a
declaration.
special
(special var1 var2 ...) specifies that all of the variables named are to
be considered special. This specifier affects variable bindings but also
pervasively affects references. All variable bindings affected are made to
be dynamic bindings, and affected variable references refer to the current
dynamic binding rather than to the current local binding. For example:
(defun hack (thing *mod*) ;The binding of the parameter
(declare (special *mod*)) ; *mod* is visible to hack1,
(hack1 (car thing))) ; but not that of thing
(defun hack1 (arg)
(declare (special *mod*)) ;Declare references to *mod*
; within hack1 to be special
(if (atom arg) *mod*
(cons (hack1 (car arg)) (hack1 (cdr arg)))))
Note that it is conventional, though not required, to give special
variables names that begin and end with an asterisk.
A special declaration does not affect bindings pervasively. Inner bindings
of a variable implicitly shadow a special declaration and must be
explicitly re-declared to be special. (However, a special proclamation
does pervasively affect bindings; this exception is made for reasons of
convenience and compatibility with MacLisp.) For example:
(proclaim '(special x)) ;x is always special
(defun example (x y)
(declare (special y))
(let ((y 3) (x (* x 2)))
(print (+ y (locally (declare (special y)) y)))
(let ((y 4)) (declare (special y)) (foo x))))
In the contorted code above, the outermost and innermost bindings of y are
special and therefore dynamically scoped, but the middle binding is
lexically scoped. The two arguments to + are different, one being the
value, which is 3, of the lexically bound variable y, and the other being
the value of the special variable named y (a binding of which happens,
coincidentally, to lexically surround it at an outer level). All the
bindings of x and references to x are special, however, because of the
proclamation that x is always special.
As a matter of style, use of special proclamations should be avoided. The
defvar and defparameter macros are the conventional means for proclaiming
special variables in a program.
type
(type type var1 var2 ...) affects only variable bindings and specifies
that the variables mentioned will take on values only of the specified
type. In particular, values assigned to the variables by setq, as well as
the initial values of the variables, must be of the specified type.
[change_begin]
X3J13 voted in January 1989 (DECLARE-TYPE-FREE) to alter the
interpretation of type declarations. They are not to be construed to
affect ``only variable bindings.'' The new rule for a declaration of a
variable to have a specified type is threefold:
o It is an error if, during the execution of any reference to that
variable within the scope of the declaration, the value of the
variable is not of the declared type.
o It is an error if, during the execution of a setq of that variable
within the scope of the declaration, the new value for the variable
is not of the declared type.
o It is an error if, at any moment that execution enters the scope of
the declaration, the value of the variable is not of the declared
type.
One may think of a type declaration (declare (type face bodoni)) as
implicitly changing every reference to bodoni within the scope of the
declaration to (the face bodoni); changing every expression exp assigned
to bodoni within the scope of the declaration to (the face exp); and
implicitly executing (the face bodoni) every time execution enters the
scope of the declaration.
These new rules make type declarations much more useful. Under first
edition rules, a type declaration was useless if not associated with a
variable binding; declarations such as in
(locally
(declare (type (byte 8) x y))
(+ x y))
at best had no effect and at worst were erroneous, depending on one's
interpretation of the first edition. Under the interpretation approved by
X3J13, such declarations have ``the obvious natural interpretation.''
X3J13 noted that if nested type declarations refer to the same variable,
then all of them have effect; the value of the variable must be a member
of the intersection of the declared types.
Nested type declarations could occur as a result of either macro expansion
or carefully crafted code. There are three cases. First, the inner type
might be a subtype of the outer one:
(defun compare (apples oranges)
(declare (type number apples oranges))
(cond ((typep apples 'fixnum)
;; The programmer happens to know that, thanks to
;; constraints imposed by the caller, if APPLES
;; is a fixnum, then ORANGES will be also, and
;; therefore wishes to avoid the unnecessary cost
;; of checking ORANGES. Nevertheless the compiler
;; should be informed to allow it to optimize code.
(locally (declare (type fixnum apples oranges)))
;; Maybe the compiler could have figured
;; out by flow analysis that APPLES must
;; be a fixnum here, but it doesn't hurt
;; to say it explicitly.
(< apples oranges)))
((or (complex apples)
(complex oranges))
(error "Not yet implemented. Sorry."))
...))
This is the case most likely to arise in code written completely by hand.
Second, the outer type might be a subtype of the inner one. In this case
the inner declaration has no additional practical effect, but it is
harmless. This is likely to occur if code declares a variable to be of a
very specific type and then passes it to a macro that then declares it to
be of a less specific type.
Third, the inner and outer declarations might be for types that overlap,
neither being a subtype of the other. This is likely to occur only as a
result of macro expansion. For example, user code might declare a variable
to be of type integer, and a macro might later declare it to be of type
(or fixnum package); in this case a compiler could intersect the two types
to determine that in this instance the variable may hold only fixnums.
The reader should note that the following code fragment is, perhaps
astonishingly, not in error under the interpretation approved by X3J13:
(let ((james .007)
(maxwell 86))
(flet ((spy-swap ()
(rotatef james maxwell)))
(locally (declare (integer maxwell))
(spy-swap)
(view-movie "The Sound of Music")
(spy-swap)
maxwell)))
=> 86 (after a couple of hours of Julie Andrews)
The variable maxwell is declared to be an integer over the scope of the
type declaration, not over its extent. Indeed maxwell takes on the
non-integer value .007 while the Trapp family make their escape, but
because no reference to maxwell within the scope of the declaration ever
produces a non-integer value, the code is correct.
Now the assignment to maxwell during the first call to spy-swap, and the
reference to maxwell during the second call, do involve non-integer
values, but they occur within the body of spy-swap, which is not in the
scope of the type declaration! One could put the declaration in a
different place so as to include spy-swap in the scope:
(let ((james .007)
(maxwell 86))
(locally (declare (integer maxwell))
(flet ((spy-swap ()
(rotatef james maxwell)))
(spy-swap) ;Bug!
(view-movie "The Sound of Music")
(spy-swap)
maxwell)))
and then the code is indeed in error.
X3J13 also voted in January 1989 (FUNCTION-TYPE-ARGUMENT-TYPE-SEMANTICS)
to alter the meaning of the function type specifier when used in type
declarations (see section 4.5).
[change_end]
type
(type var1 var2 ...) is an abbreviation for (type type var1 var2 ...),
provided that type is one of the symbols appearing in table 4-1.
[change_begin]
Observe that this covers the particularly common case of declaring numeric
variables:
(declare (single-float mass dx dy dz)
(double-float acceleration sum))
In many implementations there is also some advantage to declaring
variables to have certain specialized vector types such as base-string.
[change_end]
ftype
(ftype type function-name-1 function-name-2 ...) specifies that the named
functions will be of the functional type type, an example of which
follows. For example:
(declare (ftype (function (integer list) t) nth)
(ftype (function (number) float) sin cos))
Note that rules of lexical scoping are observed; if one of the functions
mentioned has a lexically apparent local definition (as made by flet or
labels), then the declaration applies to that local definition and not to
the global function definition.
[change_begin]
X3J13 voted in March 1989 (FUNCTION-NAME) to extend ftype declaration
specifiers to accept any function-name (a symbol or a list whose car is
setf - see section 7.1). Thus one may write
(declaim (ftype (function (list) t) (setf cadr)))
to indicate the type of the setf expansion function for cadr.
X3J13 voted in January 1989 (FUNCTION-TYPE-ARGUMENT-TYPE-SEMANTICS) to
alter the meaning of the function type specifier when used in ftype
declarations (see section 4.5).
[change_end]
[old_change_begin]
function
(function name arglist result-type1 result-type2 ...) is entirely
equivalent to
(ftype (function arglist result-type1 result-type2 ...) name)
but may be more convenient for some purposes. For example:
(declare (function nth (integer list) t)
(function sin (number) float)
(function cos (number) float))
The syntax mildly resembles that of defun: a function-name, then an
argument list, then a specification of results.
Note that rules of lexical scoping are observed; if one of the functions
mentioned has a lexically apparent local definition (as made by flet or
labels), then the declaration applies to that local definition and not to
the global function definition.
[old_change_end]
[change_begin]
X3J13 voted in January 1989 (DECLARE-FUNCTION-AMBIGUITY) to remove this
interpretation of the function declaration specifier from the language.
Instead, a declaration specifier
(function var1 var2 ...)
is to be treated simply as an abbreviation for
(type function var1 var2 ...)
just as for all other symbols appearing in table 4-1.
X3J13 noted that although function appears in table 4-1, the first edition also
discussed it explicitly, with a different meaning, without noting whether the
differing interpretation was to replace or augment the interpretation regarding
table 4-1. Unfortunately there is an ambiguous case: the declaration
(declare (function foo nil string))
can be construed to abbreviate either
(declare (ftype (function () string) foo))
or
(declare (type function foo nil string))
The latter could perhaps be rejected on semantic grounds: it would be an error
to declare nil, a constant, to be of type function. In any case, X3J13
determined that the ice was too thin here; the possibility of confusion is not
worth the convenience of an abbreviation for ftype declarations. The change
also makes the language more consistent.
[change_end]
inline
(inline function1 function2 ...) specifies that it is desirable for the
compiler to open-code calls to the specified functions; that is, the code
for a specified function should be integrated into the calling routine,
appearing in-line in place of a procedure call. This may achieve extra
speed at the expense of debuggability (calls to functions compiled in-line
cannot be traced, for example). This declaration is pervasive. Remember
that a compiler is free to ignore this declaration.
Note that rules of lexical scoping are observed; if one of the functions
mentioned has a lexically apparent local definition (as established by
flet or labels), then the declaration applies to that local definition and
not to the global function definition.
[change_begin]
X3J13 voted in October 1988 (PROCLAIM-INLINE-WHERE) to clarify that
during compilation the inline declaration specifier serves two distinct
purposes: it indicates not only that affected calls to the specified
functions should be expanded in-line, but also that affected definitions
of the specified functions must be recorded for possible use in performing
such expansions.
Looking at it the other way, the compiler is not required to save function
definitions against the possibility of future expansions unless the
functions have already been proclaimed to be inline. If a function is
proclaimed (or declaimed) inline before some call to that function but the
current definition of that function was established before the
proclamation was processed, it is implementation-dependent whether that
call will be expanded in-line. (Of course, it is implementation-dependent
anyway, because a compiler is always free to ignore inline declaration
specifiers. However, the intent of the committee is clear: for best
results, the user is advised to put any inline proclamation of a function
before any definition of or call to that function.)
Consider these examples:
(defun huey (x) (+ x 100)) ;Compiler need not remember this
(declaim (inline huey dewey))
(defun dewey (y) (huey (sqrt y))) ;Call to huey unlikely to be expanded
(defun louie (z) (dewey (/ z))) ;Call to dewey likely to be expanded
X3J13 voted in March 1989 (FUNCTION-NAME) to extend inline declaration
specifiers to accept any function-name (a symbol or a list whose car is
setf - see section 7.1). Thus one may write (declare (inline (setf cadr)))
to indicate that the setf expansion function for cadr should be compiled
in-line.
[change_end]
notinline
(notinline function1 function2 ...) specifies that it is undesirable to
compile the specified functions in-line. This declaration is pervasive. A
compiler is not free to ignore this declaration.
Note that rules of lexical scoping are observed; if one of the functions
mentioned has a lexically apparent local definition (as made by flet or
labels), then the declaration applies to that local definition and not to
the global function definition.
[change_begin]
X3J13 voted in March 1989 (FUNCTION-NAME) to extend notinline
declaration specifiers to accept any function-name (a symbol or a list
whose car is setf - see section 7.1). Thus one may write (declare
(notinline (setf cadr))) to indicate that the setf expansion function for
cadr should not be compiled in-line.
X3J13 voted in January 1989 (ALLOW-LOCAL-INLINE) to clarify that the
proper way to define a function gnards that is not inline by default, but
for which a local declaration (declare (inline gnards)) has half a chance
of actually compiling gnards in-line, is as follows:
(declaim (inline gnards))
(defun gnards ...)
(declaim (notinline gnards))
The point is that the first declamation informs the compiler that the
definition of gnards may be needed later for in-line expansion, and the
second declamation prevents any expansions unless and until it is
overridden.
While an implementation is never required to perform in-line expansion,
many implementations that do support such expansion will not process
inline requests successfully unless definitions are written with these
proclamations in the manner shown above.
[change_end]
ignore
(ignore var1 var2 ... varn) affects only variable bindings and specifies
that the bindings of the specified variables are never used. It is
desirable for a compiler to issue a warning if a variable so declared is
ever referred to or is also declared special, or if a variable is lexical,
never referred to, and not declared to be ignored.
optimize
(optimize (quality1 value1) (quality2 value2)...) advises the compiler
that each quality should be given attention according to the specified
corresponding value. A quality is a symbol; standard qualities include
speed (of the object code), space (both code size and run-time space),
safety (run-time error checking), and compilation-speed (speed of the
compilation process).
[change_begin]
X3J13 voted in October 1988 (OPTIMIZE-DEBUG-INFO) to add the standard
quality debug (ease of debugging).
[change_end]
Other qualities may be recognized by particular implementations. A value
should be a non-negative integer, normally in the range 0 to 3. The value
0 means that the quality is totally unimportant, and 3 that the quality is
extremely important; 1 and 2 are intermediate values, with 1 the
``normal'' or ``usual'' value. One may abbreviate (quality 3) to simply
quality. This declaration is pervasive. For example:
(defun often-used-subroutine (x y)
(declare (optimize (safety 2)))
(error-check x y)
(hairy-setup x)
(do ((i 0 (+ i 1))
(z x (cdr z)))
((null z) i)
;; This inner loop really needs to burn.
(declare (optimize speed))
(declare (fixnum i))
)))
declaration
(declaration name1 name2 ...) advises the compiler that each namej is a
valid but non-standard declaration name. The purpose of this is to tell
one compiler not to issue warnings for declarations meant for another
compiler or other program processor.
[old_change_begin]
This kind of declaration may be used only as a proclamation. For example:
(proclaim '(declaration author
target-language
target-machine))
(proclaim '(target-language ada))
(proclaim '(target-machine IBM-650))
(defun strangep (x)
(declare (author "Harry Tweeker"))
(member x '(strange weird odd peculiar)))
[old_change_end]
[change_begin]
X3J13 voted in June 1989 (PROCLAIM-ETC-IN-COMPILE-FILE) to introduce the
new macro declaim, which is guaranteed to be recognized appropriately by
the compiler and is often more convenient than proclaim for establishing
global declarations.
The declaration declaration specifier may be used with declaim as well as
proclaim. The preceding examples would be better written using declaim, to
ensure that the compiler will process them properly.
(declaim (declaration author
target-language
target-machine))
(declaim (target-language ada)
(target-machine IBM-650))
(defun strangep (x)
(declare (author "Harry Tweeker"))
(member x '(strange weird odd peculiar)))
X3J13 voted in March 1989 (DYNAMIC-EXTENT) to introduce a new declaration
specifier dynamic-extent for variables, and voted in June 1989
(DYNAMIC-EXTENT-FUNCTION) to extend it to handle function-names as well.
dynamic-extent
(dynamic-extent item1 item2 ... itemn) declares that certain variables or
function-names refer to data objects whose extents may be regarded as
dynamic; that is, the declaration may be construed as a guarantee on the
part of the programmer that the program will behave correctly even if the
data objects have only dynamic extent rather than the usual indefinite
extent.
Each item may be either a variable name or (function f) where f is a
function-name (see section 7.1). (Of course, (function f) may be
abbreviated in the usual way as #'f.)
It is permissible for an implementation simply to ignore this declaration.
In implementations that do not ignore it, the compiler (or interpreter) is
free to make whatever optimizations are appropriate given this
information; the most common optimization is to stack-allocate the initial
value of the object. The data types that can be optimized in this manner
may vary from implementation to implementation.
The meaning of this declaration can be stated more precisely. We say that
object x is an otherwise inaccessible part of y if and only if making y
inaccessible would make x inaccessible. (Note that every object is an
otherwise inaccessible part of itself.) Now suppose that construct c
contains a dynamic-extent declaration for variable (or function) v (which
need not be bound by c). Consider the values taken on by v during the
course of some execution of c. The declaration asserts that if some object
x is an otherwise inaccessible part of whenever becomes the value of
v, then just after execution of c terminates x will be either inaccessible
or still an otherwise inaccessible part of the value of v. If this
assertion is ever violated, the consequences are undefined.
In some implementations, it is possible to allocate data structures in a
way that will make them easier to reclaim than by general-purpose garbage
collection (for example, on the stack or in some temporary area). The
dynamic-extent declaration is designed to give the implementation the
information necessary to exploit such techniques.
For example, in the code fragment
(let ((x (list 'a1 'b1 'c1))
(y (cons 'a2 (cons 'b2 (cons 'c2 'd2)))))
(declare (dynamic-extent x y))
...)
it is not difficult to prove that the otherwise inaccessible parts of x
include the three conses constructed by list, and that the otherwise
inaccessible parts of y include three other conses manufactured by the
three calls to cons. Given the presence of the dynamic-extent declaration,
a compiler would be justified in stack-allocating these six conses and
reclaiming their storage on exit from the let form.
Since stack allocation of the initial value entails knowing at the
object's creation time that the object can be stack-allocated, it is not
generally useful to declare dynamic-extent for variables that have no
lexically apparent initial value. For example,
(defun f ()
(let ((x (list 1 2 3)))
(declare (dynamic-extent x))
...))
would permit a compiler to stack-allocate the list in x. However,
(defun g (x) (declare (dynamic-extent x)) ...)
(defun f () (g (list 1 2 3)))
could not typically permit a similar optimization in f because of the
possibility of later redefinition of g. Only an implementation careful
enough to recompile f if the definition of g were to change incompatibly
could stack-allocate the list argument to g in f.
Other interesting cases are
(declaim (inline g))
(defun g (x) (declare (dynamic-extent x)) ...)
(defun f () (g (list 1 2 3)))
and
(defun f ()
(flet ((g (x) (declare (dynamic-extent x)) ...))
(g (list 1 2 3))))
In each case some compilers might realize the optimization is possible and
others might not.
An interesting variant of this is the so-called stack-allocated rest list,
which can be achieved (in implementations supporting the optimization) by
(defun f (&rest x)
(declare (dynamic-extent x))
...)
Note here that although the initial value of x is not explicitly present,
nevertheless in the usual implementation strategy the function f is
responsible for assembling the list for x from the passed arguments, so
the f function can be optimized by a compiler to construct a
stack-allocated list instead of a heap-allocated list.
Some Common Lisp functions take other functions as arguments; frequently
the argument function is a so-called downward funarg, that is, a
functional argument that is passed only downward and whose extent may
therefore be dynamic.
(flet ((gd (x) (atan (sinh x))))
(declare (dynamic-extent #'gd)) ;mapcar won't hang on to gd
(mapcar #'gd my-list-of-numbers))
The following three examples are in error, since in each case the value of
x is used outside of its extent.
(length (let ((x (list 1 2 3)))
(declare (dynamic-extent x))
x)) ;Wrong
The preceding code is obviously incorrect, because the cons cells making
up the list in x might be deallocated (thanks to the declaration) before
length is called.
(length (list (let ((x (list 1 2 3)))
(declare (dynamic-extent x))
x))) ;Wrong
In this second case it is less obvious that the code is incorrect, because
one might argue that the cons cells making up the list in x have no effect
on the result to be computed by length. Nevertheless the code briefly
violates the assertion implied by the declaration and is therefore
incorrect. (It is not difficult to imagine a perfectly sensible
implementation of a garbage collector that might become confused by a cons
cell containing a dangling pointer to a list that was once stack-allocated
but then deallocated.)
(progn (let ((x (list 1 2 3)))
(declare (dynamic-extent x))
x) ;Wrong
(print "Six dollars is your change have a nice day NEXT!"))
In this third case it is even less obvious that the code is incorrect,
because the value of x returned from the let construct is discarded right
away by the progn. Indeed it is, but ``right away'' isn't fast enough. The
code briefly violates the assertion implied by the declaration and is
therefore incorrect. (If the code is being interpreted, the interpreter
might hang on to the value returned by the let for some time before it is
eventually discarded.)
Here is one last example, one that has little practical import but is
theoretically quite instructive.
(dotimes (j 10)
(declare (dynamic-extent j))
(setq foo 3) ;Correct
(setq foo j)) ;Erroneous-but why? (see text)
Since j is an integer by the definition of dotimes, but eq and eql are not
necessarily equivalent for integers, what are the otherwise inaccessible
parts of j, which this declaration requires the body of the dotimes not to
``save''? If the value of j is 3, and the body does (setq foo 3), is that
an error? The answer is no, but the interesting thing is that it depends
on the implementation-dependent behavior of eq on numbers. In an
implementation where eq and eql are equivalent for 3, then 3 is not an
otherwise inaccessible part because (eq j (+ 2 1)) is true, and therefore
there is another way to access the object besides going through j. On the
other hand, in an implementation where eq and eql are not equivalent for
3, then the particular 3 that is the value of j is an otherwise
inaccessible part, but any other 3 is not. Thus (setq foo 3) is valid but
(setq foo j) is erroneous. Since (setq foo j) is erroneous in some
implementations, it is erroneous in all portable programs, but some other
implementations may not be able to detect the error. (If this conclusion
seems strange, it may help to replace 3 everywhere in the preceding
argument with some obvious bignum such as 375374638837424898243 and to
replace 10 with some even larger bignum.)
The dynamic-extent declaration should be used with great care. It makes
possible great performance improvements in some situations, but if the
user misdeclares something and consequently the implementation returns a
pointer into the stack (or stores it in the heap), an undefined situation
may result and the integrity of the Lisp storage mechanism may be
compromised. Debugging these situations may be tricky. Users who have
asked for this feature have indicated a willingness to deal with such
problems; nevertheless, I do not encourage casual users to use this
declaration.
[change_end]
An implementation is free to support other (implementation-dependent)
declaration specifiers as well. On the other hand, a Common Lisp compiler is
free to ignore entire classes of declaration specifiers (for example,
implementation-dependent declaration specifiers not supported by that
compiler's implementation), except for the declaration declaration specifier.
Compiler implementors are encouraged, however, to program the compiler to issue
by default a warning if the compiler finds a declaration specifier of a kind it
never uses. Such a warning is required in any case if a declaration specifier
is not one of those defined above and has not been declared in a declaration
declaration.
-------------------------------------------------------------------------------
9.3. Type Declaration for Forms
Frequently it is useful to declare that the value produced by the evaluation of
some form will be of a particular type. Using declare one can declare the type
of the value held by a bound variable, but there is no easy way to declare the
type of the value of an unnamed form. For this purpose the the special form is
defined; (the type form) means that the value of form is declared to be of type
type.
[Special Form]
the value-type form
The form is evaluated; whatever it produces is returned by the the form. In
addition, it is an error if what is produced by the form does not conform to
the data type specified by value-type (which is not evaluated). (A given
implementation may or may not actually check for this error. Implementations
are encouraged to make an explicit error check when running interpretively.) In
effect, this declares that the user undertakes to guarantee that the values of
the form will always be of the specified type. For example:
(the string (copy-seq x)) ;The result will be a string
(the integer (+ x 3)) ;The result of + will be an integer
(+ (the integer x) 3) ;The value of x will be an integer
(the (complex rational) (* z 3))
(the (unsigned-byte 8) (logand x mask))
The values type specifier may be used to indicate the types of multiple values:
(the (values integer integer) (floor x y))
(the (values string t)
(gethash the-key the-string-table))
[change_begin]
X3J13 voted in June 1989 (THE-AMBIGUITY) to clarify that value-type may be
any valid type specifier whatsoever. The point is that a type specifier need
not be one suitable for discrimination but only for declaration.
In the case that the form produces exactly one value and value-type is not a
values type specifier, one may describe a the form as being entirely equivalent
to
(let ((#1=#:temp form)) (declare (type value-type #1#)) #1#)
A more elaborate expression could be written to describe the case where
value-type is a values type specifier.
[change_end]
-------------------------------------------------------------------------------
Compatibility note: This construct is borrowed from the Interlisp DECL package;
Interlisp, however, allows an implicit progn after the type specifier rather
than just a single form. The MacLisp fixnum-identity and flonum-identity
constructs can be expressed as (the fixnum x) and (the single-float x).
-------------------------------------------------------------------------------